﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WordNet.Core.ElementContracts;
using WordNet.Core.Extensions;

namespace WordNet.Core
{
    public class Word:IWord
    {
        private readonly IWordID _id;
        private readonly ISynset _synset;
        private readonly ISenseKey _senseKey;
        private readonly AdjectiveMarker _adjMarker;
        private readonly int _lexID;
        private readonly List<IVerbFrame> _frames;
        private readonly List<IWordID> _allWords;
        private readonly Dictionary<IPointer, List<IWordID>> _wordMap;

        public Word(ISynset synset, int number, String lemma, int lexID, AdjectiveMarker adjMarker,
            List<IVerbFrame> frames, Dictionary<IPointer, List<IWordID>> pointers)
            : this(synset, new WordID(synset.ID, number, lemma), lexID, adjMarker, frames, pointers)
        { }

        public Word(ISynset synset, IWordID id, int lexID, AdjectiveMarker adjMarker,
               List<IVerbFrame> frames, Dictionary<IPointer, List<IWordID>> pointers)
        {

            // check arguments
            if (synset == null)
                throw new ArgumentNullException();
            if (id == null)
                throw new ArgumentNullException();
            lexID.CheckLexicalID();
            if (synset.PartOfSpeech != POS.ADJECTIVE && adjMarker != null)
                throw new ArgumentException();

            // fill synset map
            HashSet<IWordID> hiddenSet = null;
            Dictionary<IPointer, List<IWordID>> hiddenMap = null;
            if (pointers != null)
            {
                hiddenSet = new HashSet<IWordID>();
                hiddenMap = new Dictionary<IPointer, List<IWordID>>(new PointerIndexKeyComparator());
                foreach (var entry in pointers)
                {
                    if (entry.Value != null && entry.Value.Count() != 0)
                    {
                        hiddenMap.Add(entry.Key, new List<IWordID>(entry.Value));
                        foreach (var val in entry.Value)
                            hiddenSet.Add(val);
                    }
                }
            }

            // field assignments
            _synset = synset;
            _id = id;
            _lexID = lexID;
            _adjMarker = adjMarker;
            _senseKey = new SenseKey(id.Lemma, lexID, synset);
            _allWords = (hiddenSet != null && hiddenSet.Count() != 0) ?
                            new List<IWordID>(hiddenSet) : new List<IWordID>();
            _wordMap = (hiddenMap != null && hiddenMap.Count() != 0) ?
                    hiddenMap : new Dictionary<IPointer, List<IWordID>>();
            _frames = (frames == null || frames.Count() == 0) ?
                        new List<IVerbFrame>() : new List<IVerbFrame>(frames);
        }

        public string Lemma
        {
            get { return _id.Lemma; }
        }

        public ISynset Synset
        {
            get { return _synset; }
        }

        public ISenseKey SenseKey
        {
            get { return _senseKey; }
        }

        public int LexicalID
        {
            get { return _lexID; }
        }

        public Dictionary<IPointer, List<IWordID>> GetRelatedMap()
        {
            return _wordMap;
        }

        public List<IWordID> GetRelatedWords(IPointer ptr)
        {
            if (_wordMap == null)
                return null;
            if (!_wordMap.ContainsKey(ptr))
                return null;
            List<IWordID> result = _wordMap[ptr];
            return (result == null) ? new List<IWordID>() : result;
        }

        public List<IWordID> GetRelatedWords()
        {
            return (_wordMap == null) ?
                     new List<IWordID>() : _allWords;
        }

        public List<IVerbFrame> GetVerbFrames()
        {
            return _frames;
        }

        public AdjectiveMarker AdjectiveMarker
        {
            get { return _adjMarker; }
        }

        public POS PartOfSpeech
        {
            get { return _id.SynsetID.PartOfSpeech; }
        }

        public IWordID ID
        {
            get { return _id; }
        }

        public override string ToString()
        {
            if (_id.WordNumber == 0)
            {
                return "W-" + _id.SynsetID.ToString().Substring(4) + "-?-"
                        + _id.Lemma;
            }
            else
            {
                return "W-" + _id.SynsetID.ToString().Substring(4) + "-"
                        + _id.WordNumber + "-" + _id.Lemma;
            }
        }

        public override int GetHashCode()
        {
            const int PRIME = 31;
            int result = 0;
            foreach(var f in _frames)
                result = PRIME + f.GetHashCode();
            result = PRIME * result + _id.GetHashCode();
            result = PRIME * result + _lexID;
            result = PRIME * result + ((_adjMarker == null) ? 0 : _adjMarker.GetHashCode());
            result = PRIME * result + _wordMap.GetHashCode();
            return result;
        }

        public override bool Equals(object obj)
        {
            if (Object.ReferenceEquals(obj, null))
                return false;
            if (Object.ReferenceEquals(this, obj))
                return true;

            IWord that = obj as IWord;
            if(that == null)
                return false;
            
            // check id
            if (!this._id.Equals(that.ID))
                return false;

            // check lexical id
            if (this._lexID != that.LexicalID)
                return false;

            // check adjective marker
            if (this._adjMarker == null)
            {
                if (that.AdjectiveMarker != null)
                    return false;
            }
            else if (!_adjMarker.Equals(that.AdjectiveMarker))
                return false;

            // check frames
            if (!_frames.SequenceEqual(that.GetVerbFrames()))
                return false;
            
            // check map
            if (!_wordMap.Equals(that.GetRelatedMap()))
                return false;
           
            return true;
        }
        
        
     }
}
